import {
  Injectable,
  NotFoundException,
  ConflictException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { VideoCategory } from 'src/entities/video-category.entity';
import { Repository } from 'typeorm';
import { CreateVideoCategoryDto } from './dto/create-video-category.dto';
import { UpdateVideoCategoryDto } from './dto/update-video-category.dto';
import { Video } from 'src/entities/video.entity';
import { InjectRedis } from '@nestjs-modules/ioredis';
import Redis from 'ioredis';
import { QueryVideoCategoryDto } from './dto/query-video-category.dto';

@Injectable()
export class VideoCategoryService {
  constructor(
    @InjectRepository(VideoCategory)
    private videoCategoryRepository: Repository<VideoCategory>,
    @InjectRepository(Video)
    private videoRepository: Repository<Video>,
    @InjectRedis() private readonly redis: Redis,
  ) {}

  async findAll(): Promise<VideoCategory[]> {
    return await this.videoCategoryRepository.find();
  }

  async findOne(id: number): Promise<VideoCategory> {
    const category = await this.videoCategoryRepository.findOne({
      where: { id },
    });
    if (!category) {
      throw new NotFoundException(`分类ID ${id} 不存在`);
    }
    return category;
  }

  async create(
    createVideoCategoryDto: CreateVideoCategoryDto,
  ): Promise<VideoCategory> {
    // 检查分类名称是否已存在
    const existingCategory = await this.videoCategoryRepository.findOne({
      where: { name: createVideoCategoryDto.name },
    });

    if (existingCategory) {
      throw new ConflictException(
        `分类名称 ${createVideoCategoryDto.name} 已存在`,
      );
    }

    const category = this.videoCategoryRepository.create(
      createVideoCategoryDto,
    );

    // 清除缓存
    await this.invalidateCategoryOptionsCache();

    return await this.videoCategoryRepository.save(category);
  }

  async update(
    id: number,
    updateVideoCategoryDto: UpdateVideoCategoryDto,
  ): Promise<VideoCategory> {
    // 检查是否存在
    await this.findOne(id);

    // 检查更新的名称是否与其他分类冲突
    if (updateVideoCategoryDto.name) {
      const existingCategory = await this.videoCategoryRepository.findOne({
        where: { name: updateVideoCategoryDto.name },
      });

      if (existingCategory && existingCategory.id !== id) {
        throw new ConflictException(
          `分类名称 ${updateVideoCategoryDto.name} 已存在`,
        );
      }
    }

    await this.videoCategoryRepository.update(id, updateVideoCategoryDto);

    // 清除缓存
    await this.invalidateCategoryOptionsCache();

    return await this.findOne(id);
  }

  async remove(id: number): Promise<void> {
    const category = await this.findOne(id);

    // 清除缓存
    await this.invalidateCategoryOptionsCache();

    await this.videoCategoryRepository.remove(category);
  }

  async getCategoryVideos(
    categoryId: number,
    options: { page: number; pageSize: number; lastId?: number },
  ): Promise<{ videos: Video[]; hasMore: boolean; total: number }> {
    // 确认分类存在
    await this.findOne(categoryId);

    const { page, pageSize, lastId } = options;
    const skip = (page - 1) * pageSize;

    // 构建基本查询
    const queryBuilder = this.videoRepository
      .createQueryBuilder('video')
      .leftJoinAndSelect('video.user', 'user')
      .where('video.category.id = :categoryId', { categoryId })
      .andWhere('video.status = :status', { status: 'approved' })
      .andWhere('video.is_delete = 0');

    // 如果提供了lastId，则从该ID之后开始查询（用于无限滚动）
    if (lastId) {
      queryBuilder.andWhere('video.video_id < :lastId', { lastId });
    }

    // 计算总数
    const total = await queryBuilder.getCount();

    // 构建混合排序算法
    // 创建一个自定义得分计算公式:
    // 分数 = 新鲜度因子 * (当前时间 - 创建时间的小时数) + 热度因子 * 点赞数
    // 较新的视频得分会更高，同时高点赞数视频也会更高
    const now = new Date();

    queryBuilder
      .orderBy(
        `(
          10000 / (TIMESTAMPDIFF(HOUR, video.created_at, '${now.toISOString()}') + 1) + 
          (video.likes_count * 0.5)
        )`,
        'DESC',
      )
      .addOrderBy('video.video_id', 'DESC')
      .limit(pageSize)
      .offset(skip);

    const videos = await queryBuilder.getMany();

    // 判断是否有更多数据
    const hasMore = total > skip + videos.length;

    return {
      videos,
      hasMore,
      total,
    };
  }

  /**
   * 获取格式化为值标签结构的分类列表
   * 用于前端表单、下拉菜单等组件
   */
  async getCategoryOptions(): Promise<{ value: string; label: string }[]> {
    // 使用Redis缓存优化性能
    const cacheKey = 'category:options';

    // 尝试从缓存获取
    const cachedData = await this.redis.get(cacheKey);
    if (cachedData) {
      return JSON.parse(cachedData);
    }

    // 缓存未命中，从数据库获取
    const categories = await this.videoCategoryRepository.find({
      select: ['id', 'name'],
      order: { id: 'ASC' },
    });

    // 转换为所需格式
    const options = categories.map((category) => ({
      value: category.id.toString(), // 将ID转为字符串，符合前端预期
      label: category.name,
    }));

    // 缓存结果，设置1小时过期
    await this.redis.set(cacheKey, JSON.stringify(options), 'EX', 3600);

    return options;
  }

  /**
   * 清除分类选项缓存
   * 在分类创建、更新和删除后调用
   */
  async invalidateCategoryOptionsCache(): Promise<void> {
    await this.redis.del('category:options');
  }

  /**
   * 分页查询视频分类
  */
  async findByPage(query: QueryVideoCategoryDto): Promise<{
    items: VideoCategory[];
    total: number;
    page: number;
    pageSize: number;
    totalPages: number;
  }> {
    const { id, name, page = 1, pageSize = 10 } = query;
    
    // 构建查询条件
    const queryBuilder = this.videoCategoryRepository.createQueryBuilder('category');
    
    // 添加过滤条件
    if (id) {
      queryBuilder.andWhere('category.id = :id', { id });
    }
    
    if (name) {
      queryBuilder.andWhere('category.name LIKE :name', { name: `%${name}%` });
    }
    
    // 计算总数
    const total = await queryBuilder.getCount();
    
    // 添加分页
    queryBuilder
      .skip((page - 1) * pageSize)
      .take(pageSize)
      .orderBy('category.id', 'DESC');
    
    // 执行查询
    const items = await queryBuilder.getMany();
    
    // 计算总页数
    const totalPages = Math.ceil(total / pageSize);
    
    return {
      items,
      total,
      page,
      pageSize,
      totalPages
    };
  }
}
